package com.license.logic.behaviorlog;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.stream.XMLEventFactory;
import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.Attribute;
import javax.xml.stream.events.Characters;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.StartDocument;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.license.common.util.Constants;
import com.license.logic.alimentation.Food;
import com.license.logic.clonalselection.Antibody;
import com.license.logic.preference.Activity;
import com.license.logic.preference.Nutrition;
import com.license.logic.profile.BehavioralProfile;
import com.license.logic.profile.EmployeeProfile;
import com.license.logic.pso.Particle;
//
//import alimentation.Food;
//
//import employeeProfile.EmployeeProfile;
//
//

public class WriteXML {
	private String externDirectoryPath;

	public StreamResult createxmlFile(String filePath)
			throws ParserConfigurationException, TransformerException {

		// write the content into xml file
		TransformerFactory transformerFactory = TransformerFactory
				.newInstance();
		Transformer transformer = transformerFactory.newTransformer();
		DOMSource source = new DOMSource();
		StreamResult result = new StreamResult(new File(filePath));
		transformer.transform(source, result);
		
		return result;
	}
	
	private void closeStream(StreamResult result){
		try {
			if (result.getOutputStream() != null)
				result.getOutputStream().close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public void saveConfig(List<EmployeeProfile> emProfilesList)
			throws Exception {
		// Create a XMLOutputFactory
		XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();
		// Create XMLEventWriter
		XMLEventWriter eventWriter;
		// Create a EventFactory
		XMLEventFactory eventFactory = XMLEventFactory.newInstance();
		XMLEvent end = eventFactory.createDTD("\n");
		// Create and write Start Tag
		StartDocument startDocument = eventFactory.createStartDocument();

		String filePath = "";
		int fileNumber = 0;
		for (EmployeeProfile profile : emProfilesList) {
			fileNumber++;
			filePath = this.externDirectoryPath + "employee" + fileNumber
					+ ".xml";
			StreamResult result = createxmlFile(filePath);

			eventWriter = outputFactory
					.createXMLEventWriter(new FileOutputStream(filePath));
			eventWriter.add(startDocument);
			eventWriter.add(end);
			int i = 0;
			createStartNodeElement(eventWriter, "employeeProfiles", null, i);
			writePersonalProfile(eventWriter, profile, i, true);
			writeMedicalProfile(eventWriter, profile, i);
			writeBehaviorlog(eventWriter, profile, i);

			createEndNodeElement(eventWriter, "employeeProfiles", null, i);
			eventWriter.add(eventFactory.createEndDocument());
			eventWriter.close();
			this.closeStream(result);
		}
		
	}
	
	public void saveEmployee(String filePath, EmployeeProfile employeeProfile) throws Exception{
		// Create a XMLOutputFactory
				XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();
				// Create XMLEventWriter
				XMLEventWriter eventWriter;
				// Create a EventFactory
				XMLEventFactory eventFactory = XMLEventFactory.newInstance();
				XMLEvent end = eventFactory.createDTD("\n");
				// Create and write Start Tag
				StartDocument startDocument = eventFactory.createStartDocument();

					StreamResult result = createxmlFile(filePath);

					eventWriter = outputFactory
							.createXMLEventWriter(new FileOutputStream(filePath));
					eventWriter.add(startDocument);
					eventWriter.add(end);
					int i = 0;
					createStartNodeElement(eventWriter, "employeeProfiles", null, i);
					writePersonalProfile(eventWriter, employeeProfile, i, true);
					writeMedicalProfile(eventWriter, employeeProfile, i);
					writeBehaviorlog(eventWriter, employeeProfile, i);

					createEndNodeElement(eventWriter, "employeeProfiles", null, i);
					eventWriter.add(eventFactory.createEndDocument());
					eventWriter.close();
					
					
					this.closeStream(result);
			}

	private void writeMedicalProfile(XMLEventWriter eventWriter,
			EmployeeProfile profile, int i) throws XMLStreamException {
		createStartNodeElement(eventWriter, "medicalProfile", null, ++i);
		createStartNodeElement(eventWriter, "medicalHistory", null, ++i);
		createNode(eventWriter, "smoker", ""
				+ profile.getMedicalProfile().getMedicalHistory().isSmoker(), i);
		createNode(eventWriter, "sbp", ""
				+ profile.getMedicalProfile().getMedicalHistory().getSbp(), i);
		createNode(eventWriter, "diabetes", ""
				+ profile.getMedicalProfile().getMedicalHistory().isDiabets(),
				i);
		if (profile.getMedicalProfile().getMedicalHistory().getActivityKalory() > 0)
		createNode(eventWriter, "activityKalory", ""
				+ profile.getMedicalProfile().getMedicalHistory().getActivityKalory(),
				i);
		if (profile.getMedicalProfile().getMedicalHistory().getNutritionKalory() > 0)
		createNode(eventWriter, "nutritionKalory", ""
				+ profile.getMedicalProfile().getMedicalHistory().getNutritionKalory(),
				i);
		createEndNodeElement(eventWriter, "medicalHistory", null, i--);
		createStartNodeElement(eventWriter, "geneticHistory", null, ++i);
		createNode(eventWriter, "familyCardHistory", ""
				+ profile.getMedicalProfile().getGeneticalHistory()
						.isFamilyCardioHistory(), i);
		createEndNodeElement(eventWriter, "geneticHistory", null, i--);
		createEndNodeElement(eventWriter, "medicalProfile", null, i--);
	}

	private void writePersonalProfile(XMLEventWriter eventWriter,
			EmployeeProfile profile, int i, boolean fullProfile)
			throws XMLStreamException {
		createStartNodeElement(eventWriter, "personalProfile", null, ++i);
		if (profile.getPersonalProfile().getName() != null )
		createNode(eventWriter, "name", ""
				+ profile.getPersonalProfile().getName(), i);
		createNode(eventWriter, "sex", profile.getPersonalProfile().getSex(), i);
		if (profile.getPersonalProfile().getBirthDate() != null )
		createNode(eventWriter, "birthdate", ""
				+ profile.getPersonalProfile().getBirthDate(), i);
		createNode(eventWriter, "age", ""
				+ profile.getPersonalProfile().getAge(), i);
		createNode(eventWriter, "weight", ""
				+ profile.getPersonalProfile().getWeight(), i);
		createNode(eventWriter, "height", ""
				+ profile.getPersonalProfile().getHeight(), i);
		if (profile.getPersonalProfile().getStartDate() != null )
		createNode(eventWriter, "startDate", ""
				+ profile.getPersonalProfile().getStartDate(), i);

		if (fullProfile) {
			// preference
			createStartNodeElement(eventWriter, "preferences", null, ++i);

			createStartNodeElement(eventWriter, "nutrition", null, ++i);
			if (profile.getPersonalProfile().getPreference().getNutritions()!= null &&
					!profile.getPersonalProfile().getPreference().getNutritions()
					.isEmpty()) {
				for (Food food : profile.getPersonalProfile().getPreference()
						.getNutritions()) {
					createNode(eventWriter, "food", food.getName(), i);
				}
			}
			createEndNodeElement(eventWriter, "nutrition", null, i--);

			createStartNodeElement(eventWriter, "physicalActivity", null, ++i);
			if (profile.getPersonalProfile().getPreference().getActivities() != null &&
					!profile.getPersonalProfile().getPreference().getActivities()
					.isEmpty()) {
				for (Activity activity : profile.getPersonalProfile()
						.getPreference().getActivities()) {
					
					createNode(eventWriter, "activity", activity.getName(), i);
				}

			}
			createEndNodeElement(eventWriter, "physicalActivity", null, i--);

			createStartNodeElement(eventWriter, "cost", null, ++i);
			createEndNodeElement(eventWriter, "cost", null, i--);

			createEndNodeElement(eventWriter, "preferences", null, i--);

			createStartNodeElement(eventWriter, "socialEconomicStatus", null,
					++i);
			createEndNodeElement(eventWriter, "socialEconomicStatus", null, i--);

		}
		createEndNodeElement(eventWriter, "personalProfile", null, i--);

	}

	private void writeBehaviorlog(XMLEventWriter eventWriter,
			EmployeeProfile profile, int i) throws XMLStreamException {
		Map<String, String> timeAttributesMap = new HashMap<String, String>();
		Map<String, String> dateAttributesMap = new HashMap<String, String>();
		createStartNodeElement(eventWriter, "behaviorlog", null, ++i);
		// create all phisical activity and nutrition for all days
		if (profile.getBehavioralProfiles() != null && !profile.getBehavioralProfiles().isEmpty()){
			for (BehavioralProfile behavioralProfile : profile
					.getBehavioralProfiles()) {
				dateAttributesMap.put("date", "" + behavioralProfile.getDay());
				createStartNodeWithAttr(eventWriter, "day", null, ++i,
						dateAttributesMap);
				// create activity log
				createStartNodeElement(eventWriter, "physicalActivity", null, ++i);
				if (!behavioralProfile.getActivityList().isEmpty()) {
					for (Activity activity : behavioralProfile.getActivityList()) {
	
						timeAttributesMap.put("timeStamptEnd", activity
								.getEndTime().toString());
						timeAttributesMap.put("timeStamptStart", activity
								.getStartTime().toString());
						createStartNodeWithAttr(eventWriter, "time", null, ++i,
								timeAttributesMap);
						createNode(eventWriter, "activity", activity.getName(), i);
						createEndNodeElement(eventWriter, "time", null, i--);
					}
				}
				createEndNodeElement(eventWriter, "physicalActivity", null, i--);
	
				// create nutrition log
				createStartNodeElement(eventWriter, "nutrition", null, ++i);
				if (!behavioralProfile.getNutritionList().isEmpty()) {
					for (List<Nutrition> nutritionPerTime : behavioralProfile
							.getNutritionList()) {
						if (!nutritionPerTime.isEmpty()) {
	
							timeAttributesMap.put("timeStamptEnd", nutritionPerTime
									.get(0).getEndTime().toString());
							timeAttributesMap.put("timeStamptStart",
									nutritionPerTime.get(0).getStartTime()
											.toString());
							createStartNodeWithAttr(eventWriter, "time", null, ++i,
									timeAttributesMap);
							for (Nutrition nutrition : nutritionPerTime) {
								createNode(eventWriter, "foodItem", "name=\""
										+ nutrition.getName() + "\" portions=\""
										+ nutrition.getPortions() + "\"", i);
							}
							createEndNodeElement(eventWriter, "time", null, i--);
						}
	
					}
				}
				createEndNodeElement(eventWriter, "nutrition", null, i--);
				createEndNodeElement(eventWriter, "day", null, i--);
			}
		}
		createEndNodeElement(eventWriter, "behaviorlog", null, i--);
	}

	private void createNode(XMLEventWriter eventWriter, String name,
			String value, int tabs) throws XMLStreamException {

		XMLEventFactory eventFactory = XMLEventFactory.newInstance();
		XMLEvent end = eventFactory.createDTD("\n");
		XMLEvent tab = eventFactory.createDTD("\t");
		// Create Start node
		StartElement sElement = eventFactory.createStartElement("", "", name);
		for (int i = 0; i - 1 < tabs; i++) {
			eventWriter.add(tab);
		}
		eventWriter.add(sElement);
		// Create Content
		Characters characters = eventFactory.createCharacters(value);
		eventWriter.add(characters);
		// Create End node
		EndElement eElement = eventFactory.createEndElement("", "", name);
		eventWriter.add(eElement);
		eventWriter.add(end);

	}

	private void createStartNodeElement(XMLEventWriter eventWriter,
			String name, String value, int tabs) throws XMLStreamException {
		XMLEventFactory eventFactory = XMLEventFactory.newInstance();
		XMLEvent end = eventFactory.createDTD("\n");
		XMLEvent tab = eventFactory.createDTD("\t");
		// Create Start node
		StartElement sElement = eventFactory.createStartElement("", "", name);
		for (int i = 0; i < tabs; i++) {
			eventWriter.add(tab);
		}
		eventWriter.add(sElement);
		eventWriter.add(end);
	}

	private void createStartNodeWithAttr(XMLEventWriter eventWriter,
			String name, String value, int tabs, Map<String, String> attributes)
			throws XMLStreamException {
		XMLEventFactory eventFactory = XMLEventFactory.newInstance();
		XMLEvent end = eventFactory.createDTD("\n");
		XMLEvent tab = eventFactory.createDTD("\t");
		// Create Start node
		HashSet<Attribute> attributeSet = new HashSet<Attribute>();
		for (String key : attributes.keySet()) {
			Attribute attr = eventFactory.createAttribute(key,
					attributes.get(key));
			attributeSet.add(attr);
		}
		StartElement sElement = eventFactory.createStartElement("", "", name,
				attributeSet.iterator(), null);

		for (int i = 0; i < tabs; i++) {
			eventWriter.add(tab);
		}
		eventWriter.add(sElement);
		eventWriter.add(end);
	}

	private void createEndNodeElement(XMLEventWriter eventWriter, String name,
			String value, int tabs) throws XMLStreamException {
		XMLEventFactory eventFactory = XMLEventFactory.newInstance();
		XMLEvent end = eventFactory.createDTD("\n");
		XMLEvent tab = eventFactory.createDTD("\t");
		// Create End node
		EndElement eElement = eventFactory.createEndElement("", "", name);
		for (int i = 0; i < tabs; i++) {
			eventWriter.add(tab);
		}
		eventWriter.add(eElement);
		eventWriter.add(end);
	}

	public String getExternDirectoryPath() {
		return externDirectoryPath;
	}

	public void setExternDirectoryPath(String externDirectoryPath) {
		this.externDirectoryPath = externDirectoryPath;
	}

	public void saveEmployeeManualGenerate(List<EmployeeProfile> emProfilesList)
			throws Exception {
		// Create a XMLOutputFactory
		XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();
		// Create XMLEventWriter
		XMLEventWriter eventWriter;
		// Create a EventFactory
		XMLEventFactory eventFactory = XMLEventFactory.newInstance();
		XMLEvent end = eventFactory.createDTD("\n");
		// Create and write Start Tag
		StartDocument startDocument = eventFactory.createStartDocument();

		String filePath = "";
		int fileNumber = 0;
		for (EmployeeProfile profile : emProfilesList) {
			fileNumber++;
			filePath = this.externDirectoryPath + "test" + fileNumber + ".xml";
			StreamResult result = createxmlFile(filePath);

			eventWriter = outputFactory
					.createXMLEventWriter(new FileOutputStream(filePath));
			eventWriter.add(startDocument);
			eventWriter.add(end);
			int i = 0;
			createStartNodeElement(eventWriter, "employeeProfiles", null, i);
			writePersonalProfile(eventWriter, profile, i, false);
			writeMedicalProfile(eventWriter, profile, i);

			createEndNodeElement(eventWriter, "employeeProfiles", null, i);
			eventWriter.add(eventFactory.createEndDocument());
			eventWriter.close();
			this.closeStream(result);
		}
	}
	
	public void printParticleDiet(List<Particle> particles, String employeeName) throws ParserConfigurationException, TransformerException, FileNotFoundException, XMLStreamException{
		
		// Create a XMLOutputFactory
		XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();
		// Create XMLEventWriter
		XMLEventWriter eventWriter;
		// Create a EventFactory
		XMLEventFactory eventFactory = XMLEventFactory.newInstance();
		XMLEvent end = eventFactory.createDTD("\n");
		// Create and write Start Tag
		StartDocument startDocument = eventFactory.createStartDocument();
		String filePath = Constants.PSO_GENERATED_MENUS_PATH + employeeName + Constants.XML_EXTENSION;
		int dayNumber = 1;
		StreamResult result = createxmlFile(filePath);

		eventWriter = outputFactory
				.createXMLEventWriter(new FileOutputStream(filePath));
		eventWriter.add(startDocument);
		eventWriter.add(end);
		
		for (Particle particle : particles){
			int i = 0;
			createStartNodeElement(eventWriter, "day " + dayNumber++, null, i);
			writeParticleValues(eventWriter, particle, i);
			createEndNodeElement(eventWriter, "day " + dayNumber, null, i);
		}
		eventWriter.add(eventFactory.createEndDocument());
		eventWriter.close();
		
		this.closeStream(result);
	}
	
public void printAntibodyDiet(List<Antibody> antibodies, String employeeName) throws ParserConfigurationException, TransformerException, FileNotFoundException, XMLStreamException{
		
		// Create a XMLOutputFactory
		XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();
		// Create XMLEventWriter
		XMLEventWriter eventWriter;
		// Create a EventFactory
		XMLEventFactory eventFactory = XMLEventFactory.newInstance();
		XMLEvent end = eventFactory.createDTD("\n");
		// Create and write Start Tag
		StartDocument startDocument = eventFactory.createStartDocument();
		String filePath = Constants.CLONAL_GENERATED_MENUS_PATH + employeeName + Constants.XML_EXTENSION;;
		int dayNumber = 1;
		StreamResult result = createxmlFile(filePath);

		eventWriter = outputFactory
				.createXMLEventWriter(new FileOutputStream(filePath));
		eventWriter.add(startDocument);
		eventWriter.add(end);
		
		for (Antibody antibody : antibodies){
			int i = 0;
			createStartNodeElement(eventWriter, "day " + dayNumber++, null, i);
			writeAntibodyValues(eventWriter, antibody, i);
			createEndNodeElement(eventWriter, "day " + dayNumber, null, i);
		}
		eventWriter.add(eventFactory.createEndDocument());
		eventWriter.close();
		
		this.closeStream(result);
	}
	
	private void createNodeForActivity(XMLEventWriter eventWriter, List<Activity> activities, int i) throws XMLStreamException{
		createStartNodeElement(eventWriter, "activities", null, ++i);
		for (Activity activity : activities){
			createNode(eventWriter, "activity", "name = \""
					+ activity.getName() + "\" minutes = \""
					+ activity.getMinutes() + "\"", i);
		}
		createEndNodeElement(eventWriter, "activities", null, i);
	}
	
	private void createNodeForFood(XMLEventWriter eventWriter, List<Food> foods, int i, String mealType) throws XMLStreamException{
		createStartNodeElement(eventWriter, mealType, null, ++i);
		for (Food food : foods){
			createNode(eventWriter, "foodItem", "name = \""
					+ food.getName() + "\" portions = \""
					+ food.getPortions() + "\"", i);
		}
		createEndNodeElement(eventWriter, mealType, null, i--);
	}

	public void writeParticleValues(XMLEventWriter eventWriter,Particle particle, int i) throws XMLStreamException{
		
		// activities
		this.createNodeForActivity(eventWriter, particle.getLocalBest().getActivities(),i);
		
		
		//alimentation
		createStartNodeElement(eventWriter, "alimentation", null, ++i);
		
		this.createNodeForFood(eventWriter, particle.getLocalBest().getAlimentation().getBreakFast(), i, "breakfast");
		
		this.createNodeForFood(eventWriter, particle.getLocalBest().getAlimentation().getLunch(), i, "lunch");
		
		this.createNodeForFood(eventWriter, particle.getLocalBest().getAlimentation().getSnack(), i, "snack");
		
		this.createNodeForFood(eventWriter, particle.getLocalBest().getAlimentation().getDinner(), i, "dinner");
		
		createEndNodeElement(eventWriter, "alimentation", null, i);
		
	}
	
public void writeAntibodyValues(XMLEventWriter eventWriter,Antibody antibody, int i) throws XMLStreamException{
		
	// activities
			this.createNodeForActivity(eventWriter, antibody.getActivities(),i);
			
			
			//alimentation
			createStartNodeElement(eventWriter, "alimentation", null, ++i);
			
			this.createNodeForFood(eventWriter, antibody.getAlimentation().getBreakFast(), i, "breakfast");
			
			this.createNodeForFood(eventWriter, antibody.getAlimentation().getLunch(), i, "lunch");
			
			this.createNodeForFood(eventWriter, antibody.getAlimentation().getSnack(), i, "snack");
			
			this.createNodeForFood(eventWriter, antibody.getAlimentation().getDinner(), i, "dinner");
			
			createEndNodeElement(eventWriter, "alimentation", null, i);
		
	}
	
	public Map<String, Set<String>> getFoodExcepted(String filePath){
		Map<String, Set<String>> exceptedFoodsMap = new HashMap<String, Set<String>>();
		File fXmlFile = new File(filePath);
		DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
		DocumentBuilder dBuilder;
		try {
			dBuilder = dbFactory.newDocumentBuilder();
			Document doc = dBuilder.parse(fXmlFile);
			doc.getDocumentElement().normalize();
			NodeList foods = doc.getElementsByTagName("foods").item(0).getChildNodes();
			for (int index =1; index < foods.getLength(); index +=2){
				Node food = foods.item(index);
				String foodName = food.getNodeName().replace("_", " ");
				Set <String> exceptedFoods = new HashSet<String>();
				NodeList exceptedFoodsNodeList = food.getChildNodes();
				for (int i = 1; i < exceptedFoodsNodeList.getLength(); i +=2){
					String foodNameChild = exceptedFoodsNodeList.item(i).getTextContent().replace("_", " ");
					exceptedFoods.add(foodNameChild);
					if (!exceptedFoodsMap.keySet().contains(foodNameChild)){
						exceptedFoodsMap.put(foodNameChild,new HashSet<String>(Arrays.asList(foodName)));
					} else {
						exceptedFoodsMap.get(foodNameChild).add(foodName);
					}
				}
				if (exceptedFoodsMap.containsKey(foodName)){
					exceptedFoodsMap.get(foodName).addAll(exceptedFoods);
				} else{
					exceptedFoodsMap.put(foodName, exceptedFoods);
				}
			}
		} catch (ParserConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SAXException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return exceptedFoodsMap;
	}
	
	public void saveTestLogs (Map<Integer, List<Long>> map, String path) throws FileNotFoundException, XMLStreamException, ParserConfigurationException, TransformerException{
		XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();
		XMLEventWriter eventWriter;
		XMLEventFactory eventFactory = XMLEventFactory.newInstance();
		XMLEvent end = eventFactory.createDTD("\n");
		StartDocument startDocument = eventFactory.createStartDocument();
		
		StreamResult result = createxmlFile(path);

		eventWriter = outputFactory
				.createXMLEventWriter(new FileOutputStream(path));
		eventWriter.add(startDocument);
		eventWriter.add(end);
		int i = 0;
		createStartNodeElement(eventWriter, "employeeProfiles", null, i);
		
		
		createEndNodeElement(eventWriter, "employeeProfiles", null, i);
		eventWriter.add(eventFactory.createEndDocument());
		eventWriter.close();
		this.closeStream(result);
	}
}
